/***********************************************************************************************************************
Credits


Myself - I am coding the stuff

KJIAD - for helping me find and fix the problem in the unLink function, helped me make unWeld work between only two entities, and the reason I added strings returning if it was succesfull or not. Also helped me alot with unConstrain.

DuneD - Helped alot with minor (and some major) stuff, aswell as helped me to grasp the basics. He also uploaded this to the McBuilds-server, so I could test it in mp (and notice I needed pp).

JeremyDeath - He is your saint, as he made the pp finally work for me.

SnakeSVx - should get alot credit for helping me alot with RD3 functions (and because he made the SB3 gamemode).

The guys who created the entities, color and constraint part for egate2 functions, as I used them to learn how to code egate2 functions.

gmod lua wiki - they got a list of functions you can use.
***********************************************************************************************************************/

//An egate function for welding, put the file in the custom folder.

//The function that welds
local function Weld(self, ent1, ent2, Force, Nocollide)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then
		ent1 = GetWorldEntity()
	end
	if (!validEntity(ent2)) then
		ent2 = GetWorldEntity()
	end
	//Prop-Protection
	if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
	if (ent2 != GetWorldEntity() and !isOwner(self, ent2)) then return "Not owner" end			//check ownership
	
	if (ent1 and ent2) then
		constraint.Weld( ent1, ent2, 0, 0, Force, Nocollide )			//Welds
		return ""
	end
	return "Failed to weld"
end


//Makes a function for egate2 named weld, and you set what entities it weld, force until break (0 dont break) and welded nocollide until break (boolean)
e2function string entity:weld(entity Ent, number Force, number Nocollide)
	return Weld(self, this, Ent, Force, Nocollide)
end

//Makes a function for egate2 named weld, and you set what entities it weld and force until break (0 dont break) . It is made welded nocollide until break (boolean).
e2function string entity:weld(entity Ent, number Force)
	return Weld(self, this, Ent, Force, 1)
end

//Makes a function for egate2 named weld, and you set what entities it weld. It wont break (0 dont break) . It is made welded nocollide until break (boolean).
e2function string entity:weld(entity Ent)
	return Weld(self, this, Ent, 0, 1)
end


/********
The same three functions as above, except it is e:weld(n,n), not e:weld(e,n,n)  It welds the entity to the egate.
********/

//Makes a function for egate2 named weld, and you set what entity it weld, force until break (0 dont break) and welded nocollide until break (boolean). It welds the entity to the egate.
e2function string entity:weld(number Force, number Nocollide)
	return Weld(self, self.entity, this, Force, Nocollide)
end

//Makes a function for egate2 named weld, and you set what entity it weld and force until break (0 dont break) . It is made welded nocollide until break (boolean). It welds the entity to the egate.
e2function string entity:weld(number Force)
	return Weld(self, self.entity, this, Force, 1)
end

//Makes a function for egate2 named weld, and you set what entity it weld. It wont break (0 dont break) . It is made welded nocollide until break (boolean). It welds the entity to the egate.
e2function string entity:weld()
	return Weld(self, self.entity, this, 0, 1)
end

/************************************************************************/

//An egate function for unwelding, put the file in the custom folder.

//The function that unwelds
local function UnWeld(self, ent1, ent2)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then
		ent1 = GetWorldEntity()
	end
	//Prop-Protection
	if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
	//It has cosntraints?
	if (!constraint.HasConstraints(ent1)) then return "The first entity has no constraints" end

	if (ent2 != "null") then
		if (!validEntity(ent2)) then
			ent2 = GetWorldEntity()
		end
		if (ent2 != GetWorldEntity()) then
			if (!isOwner(self, ent2)) then return "Not owner" end			//check ownership
			if (!constraint.HasConstraints(ent2)) then return "The second entity has no constraints" end
		end
	else
		ent2 = nil
	end
		
	if (ent1 and ent2) then
		local Table = constraint.FindConstraints(ent1,"Weld")
		for _, ent in pairs(Table) do
			if (ent.Ent2 == ent2 or ent.Ent1 == ent2) then
				ent.Constraint:Remove()
				return ""
			end
		end
	else	
		constraint.RemoveConstraints( ent1, "Weld" )			//UnWelds
		return ""
	end
	return "Failed to unweld"
end

//Makes a function for egate2 named unWeld and you set what entities to unweld with eachothers.
e2function string entity:unWeld(entity Ent)
	return UnWeld(self, this, Ent)
end

//Makes a function for egate2 named unWeld and you set what entitie to unweld to everything its welded to.
e2function string entity:unWeld()
	return UnWeld(self, this, "null")
end

//Makes a function for egate2 named unWeld and unwelds the egate to everything its welded to.
e2function string unWeld()
	return UnWeld(self, self.entity, "null")
end

/************************************************************************/

//An egate function for unConstraining, put the file in the custom folder.

//The function that unConstrain
local function unConstrain(self, ent1, ent2, Type)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then
		ent1 = GetWorldEntity()
	end
	//Prop-Protection
	if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
	//It has cosntraints?
	if (!constraint.HasConstraints(ent1)) then return "The first entity has no constraints" end

	if (ent2 != "null") then
		if (!validEntity(ent2)) then
			ent2 = GetWorldEntity()
		end
		if (ent2 != GetWorldEntity()) then
			if (!isOwner(self, ent2)) then return "Not owner" end			//check ownership
			if (!constraint.HasConstraints(ent2)) then return "The second entity has no constraints" end
		end
	else
		ent2 = nil
	end
	
	if (Type) then // Fix Type naming
		local Type = Type:sub(1,1):upper() .. Type:sub(2):lower()
		if (Type == "Nocollide") then Type = "NoCollide" end
		if (Type == "Advballsocket") then Type = "AdvBallsocket" end
		if (Type == "All") then Type = "" end
	end
	
	if (ent1 and ent2) then
		for _, const in pairs(ent1.Constraints) do
			if (const.Ent2 == ent2 or const.Ent1 == ent2) then
				if (Type) then
					if (Type == const.Type) then const:Remove() end
				else
					const:Remove()
				end
			end
		end 
		return ""
	else
		if (Type) then
			constraint.RemoveConstraints(ent1,Type)
		else
			constraint.RemoveAll(ent1)
		end
		return ""
	end
	return "Failed to unconstrain"
end

//Makes a function for egate2 named unConstrain and you set what entities to unConstrain with eachothers aswell as what constraint to remove.
e2function string entity:unConstrain(entity Ent, string Type)
	return unConstrain(self, this, Ent, Type)
end

//Makes a function for egate2 named unConstrain and you set what entities to unConstrain with eachothers
e2function string entity:unConstrain(entity Ent)
	return unConstrain(self, this, Ent)
end

//Makes a function for egate2 named unConstrain and you set what entities to unConstrain with eachothers aswell as what constraint to remove.
e2function string entity:unConstrain(string Type)
	return unConstrain(self, this, 0, Type)
end

//Makes a function for egate2 named unConstrain and you set what entitie to unConstrain to everything its unConstrained to.
e2function string entity:unConstrain()
	return unConstrain(self, this, "null")
end

/************************************************************************/

//An egate function for axis, put the file in the custom folder.

//The function that axis
local function Axis(self, ent1, ent2, lPos1, lPos2, forceLimit, torqueLimit, friction, noCollide, localAxis)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then
		ent1 = GetWorldEntity()
	end
	if (!validEntity(ent2)) then
		ent2 = GetWorldEntity()
	end
	
	//Prop-Protection
	if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
	if (ent2 != GetWorldEntity() and !isOwner(self, ent2)) then return "Not owner" end			//check ownership
	
	//Check if it really is entities.
	if (ent1 and ent2) then
		
		//Turns a table datatype into vector
		lPos1 = Vector(lPos1[1],lPos1[2],lPos1[3])
		
		//Turns a table datatype into vector and treat lpos1 as a worldpos if no second pos is given
		if (!lPos2) then
			lPos1 = ent1:WorldToLocal(lPos1)
			lPos2 = Vector(0,0,0)
		else
			lPos2 = Vector(lPos2[1],lPos2[2],lPos2[3])
		end
		
		if (!localAxis) then
			localAxis = Vector(0,0,0)
		else
			localAxis = Vector(localAxis[1],localAxis[2],localAxis[3])
		end
		
		constraint.Axis(ent1, ent2, 0, 0, lPos1, lPos2, forceLimit, torqueLimit, friction, noCollide, localAxis)			//Ropes
		
		return ""
		
	end
	
	return "Failed to Axis"
end


//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2, vector lPos1, vector lPos2, forceLimit, torqueLimit, friction, noCollide, vector localAxis)
	return Axis(self,this,ent2,lPos1,lPos2,forceLimit,torqueLimit,friction,noCollide,localAxis)
end

//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2, vector lPos1, vector lPos2, forceLimit, torqueLimit, friction, noCollide)
	return Axis(self,this,ent2,lPos1,lPos2,forceLimit,torqueLimit,friction,noCollide,nil)
end

//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2, vector pos, forceLimit, torqueLimit, friction, noCollide, vector localAxis)
	return Axis(self,this,ent2,pos,nil,forceLimit,torqueLimit,friction,noCollide,localAxis)
end

//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2, vector lPos1, vector lPos2, friction)
	return Axis(self,this,ent2,lPos1,lPos2,0,0,friction,1,nil)
end

//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2, friction)
	return Axis(self,this,ent2,Vector(0,0,0),Vector(0,0,0),0,0,friction,1,nil)
end

//Makes a function for egate2 named axis
e2function string entity:axis(entity ent2)
	return Axis(self,this,ent2,Vector(0,0,0),Vector(0,0,0),0,0,0,1,nil)
end

/************************************************************************/

//An egate function for roping, put the file in the custom folder.

//The function that ropes
local function Rope(self, ent1, ent2, lPos1, lPos2, addlength, force, width, material, rigid)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then
		ent1 = GetWorldEntity()
	end
	if (!validEntity(ent2)) then
		ent2 = GetWorldEntity()
	end
	//Prop-Protection
	if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
	if (ent2 != GetWorldEntity() and !isOwner(self, ent2)) then return "Not owner" end			//check ownership
	
	if (!lPos1 and !lPos2) then
		lPos1 = Vector(0,0,0)
		lPos2 = Vector(0,0,0)
	else
		lPos1 = Vector(lPos1[1],lPos1[2],lPos1[3])
		lPos2 = Vector(lPos2[1],lPos2[2],lPos2[3])
	end
	if (ent1 and ent2) then			//Check if it really is entities.
		local length = ent1:LocalToWorld(lPos1):Distance(ent2:LocalToWorld(lPos2))
		if (rigid == 0) then
			rigid = false
		end
		constraint.Rope(ent1, ent2, 0, 0, lPos1, lPos2, length, addlength, force, width, material, rigid)			//Ropes
		return ""
	end
	return "Failed to rope"
end


//Makes a function for egate2 named rope
e2function string entity:rope(entity ent2, vector lPos1, vector lPos2, addlength, force, width, string material, rigid)
	return Rope(self,this,ent2,lPos1,lPos2,addlength,force,width,material,rigid)
end

//Makes a function for egate2 named rope
e2function string entity:rope(entity ent2, addlength, force, width)
	return Rope(self,this,ent2,false,false,addlength,force,width,"cable/rope",false)
end

//Makes a function for egate2 named rope
e2function string entity:rope(entity ent2, vector lPos1, vector lPos2)
	return Rope(self,this,ent2,lPos1,lPos2,0,0,1,"cable/rope",false)
end

//Makes a function for egate2 named rope
e2function string entity:rope(entity ent2)
	return Rope(self,this,ent2,false,false,0,0,1,"cable/rope",false)
end

/************************************************************************/

//An egate function for nocolliding, put the file in the custom folder.

//The function that nocollides.
local function NoCollide(self, ent1, ent2)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then return "First entity is not valid" end
	//Prop-Protection
	if (!isOwner(self, ent1)) then return "Not owner of first entity" end			//check ownership
	
	if (ent2 != "null") then
		if (!validEntity(ent2)) then return "Second entity is not valid" end
		if (ent2) then
			if (!isOwner(self, ent2)) then return "Not owner of second entity" end			//check ownership
		end
	else
		ent2 = nil
	end
	
	if (ent1 and !ent2) then
		ent1:SetCollisionGroup( COLLISION_GROUP_WORLD )			//Nocollides to all
		return ""
	elseif (ent1 and ent2) then
		local nocollide = constraint.NoCollide( ent1, ent2, 0, 0 )			//Nocollides
		return ""
	end
	return "Failed to nocollide"
end


//Makes a function for egate2 named noCollide and you set what entitie to nocollide with another entitie.
e2function string entity:noCollide(entity ent2)
	return NoCollide(self,this,ent2)
end

//Makes a function for egate2 named noCollide and you set what entitie to nocollide to everything but world.
e2function string entity:noCollide()
	return NoCollide(self,this,"null")
end

//Makes a function for egate2 named noCollide and nocollides the egate to everything but world.
e2function string noCollide()
	return NoCollide(self,self.entity,"null")
end

/************************************************************************/

//An egate function for removing nocollide, put the file in the custom folder.

//The function that remove nocollide.
local function Collide(self, ent1, ent2)
	//Makes non-valid entities into world
	if (!validEntity(ent1)) then return "First entity is not valid" end
	//Prop-Protection
	if (!isOwner(self, ent1)) then return "Not owner of first entity" end			//check ownership
	
	if (ent2 != "null") then
		if (!validEntity(ent2)) then return "Second entity is not valid" end
		if (!isOwner(self, ent2)) then return "Not owner of second entity" end			//check ownership
	else
		ent2 = nil
	end
	
	if (ent1 and ent2) then
		//Does ent1 have constraints?
		if (!constraint.HasConstraints(ent1)) then return "The first entity has no constraints" end
		local Table = constraint.FindConstraints(ent1,"NoCollide")
		for _, ent in pairs(Table) do
			if (ent.Ent2 == ent2 or ent.Ent1 == ent2) then
				ent.Constraint:Remove()
				local weld = constraint.Weld( ent1, ent2, 0, 0, 0, 1 )			//Welds
				weld:Remove()
				return ""
			end
		end
	elseif (ent1) then
		ent1:SetCollisionGroup( COLLISION_GROUP_NONE )			//Nocollides to all
		return ""
	end
	return "Failed to nocollide"	
end


//Makes a function for egate2 named collide and you set what entitie to remove nocollide from.
e2function string entity:collide(entity ent2)
	return Collide(self,this,ent2)
end

//Makes a function for egate2 named collide and you set what entitie to remove nocollide from.
e2function string entity:collide()
	return Collide(self,this,"null")
end

//Makes a function for egate2 named collide and removes nocollide from the egate.
e2function string collide()
	return Collide(self,self.entity,"null")
end

/**********************************************************************************************
			These functions works with and uses rd3, and therefor needs rd3 to work.
***********************************************************************************************/

if CAF.GetAddon("Resource Distribution") then
	
	//An egate function for linking Life Support 3 enitities. Put the file in the custom folder.
	
	//The function that links the stuff. 
	local function Link(self, ent1, ent2)
		//Makes non-valid entities into world
		if (!validEntity(ent1)) then return "Invalid entity" end
		if (!validEntity(ent2)) then return "Invalid entity" end
		//Prop-Protection
		if (!isOwner(self, ent1)) then return "Not owner" end			//check ownership
		if (!isOwner(self, ent2)) then return "Not owner" end			//check ownership
		
		if (!ent1 or !ent2) then return end			//Check if it really is entities.
		local length = ( ent1:GetPos(1) - ent2:GetPos()):Length()			//gets length between the entities, so you cant link further then node range.
		
		//This if-statement check if the ents is nodes or LS3 entitie or pumps. 
		if ent1.IsNode and ent2.IsNode then
			if length <= ent1.range or length <= ent2.range then
				CAF.GetAddon("Resource Distribution").linkNodes(ent1.netid, ent2.netid)
				return ""
			else
				return "Failed to link"			//Failed to link the entities.
			end
		elseif ent1.IsNode and table.Count(CAF.GetAddon("Resource Distribution").GetEntityTable(ent2)) > 0 then
			if length <= ent1.range then
				CAF.GetAddon("Resource Distribution").Link(ent2, ent1.netid)
				return ""
			else
				return "Failed to link"			//Failed to link the entities.
			end
		elseif ent2.IsNode and table.Count(CAF.GetAddon("Resource Distribution").GetEntityTable(ent1)) > 0 then
			if length <= ent2.range then
				CAF.GetAddon("Resource Distribution").Link(ent1, ent2.netid)
				return ""
			else
				return "Failed to link"			//Failed to link the entities.
			end
		elseif ent1.IsNode and ent2.IsPump then
			if length <= ent1.range then
				ent2:SetNetwork(ent1.netid)
				ent2.node = ent1
				return ""
			else
				return "Failed to link"			//Failed to link the entities.
			end
		elseif ent2.IsNode and ent1.IsPump then
			if length <= ent2.range then
				ent1:SetNetwork(ent2.netid)
				ent1.node = ent2
				return ""
			else
				return "Failed to link"			//Failed to link the entities.
			end
		end
		return "Failed to link"			//Failed to link the entities.
	end


	//Makes a function for egate2 named link and you set what entities it links for Life Support 3.
	e2function string entity:link(entity ent2)
		return Link(self,this,ent2)
	end

	/************************************************************************/

	//An egate function for unlinking Life Support 3 enitities. Put the file in the custom folder.

	/*
	CAF.GetAddon("Resource Distribution").UnlinkAllFromNode(ent1.netid)
	CAF.GetAddon("Resource Distribution").UnlinkNodes(ent1.netid, ent2.netid)
	CAF.GetAddon("Resource Distribution").Unlink(ent1)
	ent.caf.custom.rdentity:Unlink();
	*/

	//The function that unlinks the stuff. 
	local function UnLink(self, ent1, ent2)
		//Makes non-valid entities into world
		if (!validEntity(ent1)) then return "Invalid entity" end
		//Prop-Protection
		if (!isOwner(self, ent1)) then return "Not owner" end		//check ownership
		
		if (ent2 != "null") then
			if (!validEntity(ent2)) then return "Invalid entity" end
			if (!isOwner(self, ent2)) then return "Not owner" end			//check ownership
		else
			ent2 = nil
		end
	
		//This if-statement check if the ents is nodes or LS3 entitie or pumps. 
		if (ent1 and ent2) then 
			if (ent1.IsNode and ent2.IsNode) then
				CAF.GetAddon("Resource Distribution").UnlinkNodes(ent1.netid, ent2.netid)
				return ""
			elseif (ent1.IsNode) then
				ent1=ent2
			end
		end
		if (ent1) then
			if (ent1.IsNode) then
				CAF.GetAddon("Resource Distribution").UnlinkAllFromNode(ent1.netid)
				return ""
			elseif (table.Count(CAF.GetAddon("Resource Distribution").GetEntityTable(ent1)) > 0) then
				CAF.GetAddon("Resource Distribution").Unlink(ent1)
				//ent1.caf.custom.rdentity:Unlink()
				return ""
			elseif (ent1.IsPump) then
				ent1:SetNetwork(0)
				ent1.node = nil
				return ""
			end
		end
		return "Failed to unlink"
	end

	//Makes a function for egate2 named unLink and you set what entities it unlinks for Life Support 3. If both E is an entitie, they have to be nodes, which then unlinks. 
	e2function string entity:unLink(entity ent2)
		return UnLink(self,this,ent2)
	end

	//Makes a function for egate2 named unLink and you set what entitie it unlinks for Life Support 3. The ls3 entity to unlink  is E: and it will unlink it from the node (or if a node, unlink it from all entities)
	e2function string entity:unLink()
		return UnLink(self,this,"null")
	end

	/************************************************************************/

	//An EGate function for connecting pumps, aswell as set their pump-values.

	/*
	Resource strings availible (self explanatory). Just remember addons can add stuff to the list.
	"energy"
	"water"
	"nitrogen"
	"hydrogen"
	"oxygen"
	"carbon dioxide"
	"steam"
	"heavy water"
	"liquid nitrogen" (Outdated, snake removed the use of this.)
	"hot liquid nitrogen" (dunno what this is)
	*/

	local function Pump(self, ent1, ent2, res, amount)
		/*
		//Makes non-valid entities into world
		if (!validEntity(ent1)) then return "Invalid entity"	end
		if (!validEntity(ent2)) then return "Invalid entity"	end
		//Prop-Protection
		if (ent1 != GetWorldEntity() and !isOwner(self, ent1)) then return "Not owner" end			//check ownership
		if (ent2 != GetWorldEntity() and !isOwner(self, ent2)) then return "Not owner" end			//check ownership
		*/
		
		if (!ent1 or !ent1.IsPump) then return "Has to be used on pumps!"
		elseif (!ent2 or !ent2.IsPump) then
			if (res == "all") then
				// If right, automaticly finds all resources in node. 
				local netid = ent1:GetNetworkedInt("netid")
				local nettable = CAF.GetAddon("Resource Distribution").GetNetTable(netid)
				if (!nettable.resources) then return "pump is not connected to anything"
				elseif (!next(nettable.resources)) then return "Your node have no resources" end
				for k, v in pairs(nettable.resources) do
					RunConsoleCommand("SetResourceAmount", ent1:EntIndex(), k, amount)
				end
				RunConsoleCommand("PumpTurnOn", ent1:EntIndex())
				return ""
			end
			RunConsoleCommand("SetResourceAmount", ent1:EntIndex(), res, amount)
			RunConsoleCommand("PumpTurnOn", ent1:EntIndex())
			return ""
		elseif (res == "none") then
			RunConsoleCommand("LinkToPump", ent1:EntIndex(), ent2:EntIndex())
			RunConsoleCommand("PumpTurnOn", ent1:EntIndex())
			return ""
		elseif (res == "all") then
			// If right, automaticly finds all resources in node. 
			local netid = ent1:GetNetworkedInt("netid")
			local nettable = CAF.GetAddon("Resource Distribution").GetNetTable(netid)
			if (!nettable.resources) then return "pump is not connected to anything"
			elseif (!next(nettable.resources)) then return "Your node have no resources" end
			RunConsoleCommand("LinkToPump", ent1:EntIndex(), ent2:EntIndex())
			for k, v in pairs(nettable.resources) do
				RunConsoleCommand("SetResourceAmount", ent1:EntIndex(), k, amount)
			end
			RunConsoleCommand("PumpTurnOn", ent1:EntIndex())
			return ""
		else
			RunConsoleCommand("LinkToPump", ent1:EntIndex(), ent2:EntIndex())
			RunConsoleCommand("SetResourceAmount", ent1:EntIndex(), res, amount)
			RunConsoleCommand("PumpTurnOn", ent1:EntIndex())
			return ""
		end
		return "Failed to link pumps"
	end
	
	e2function string entity:pump(entity ent2, string res, amount)
		return Pump(self,this,ent2,res,amount)
	end
	
	e2function string entity:pump(string res, amount)
		return Pump(self,this,false,res,amount)
	end
	
	e2function string entity:pump(entity ent2, string res)
		return Pump(self,this,ent2,res,0)
	end
	
	e2function string entity:pump(entity ent2, amount)
		return Pump(self,this,ent2,"all",amount)
	end
	
	e2function string entity:pump(entity ent2)
		return Pump(self,this,ent2,"none",0)
	end
end